package com.youfang.sixthmedia.net;

import java.net.URI;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.Activity;
import android.os.Build;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HeaderElementIterator;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.client.HttpClient;
import ch.boye.httpclientandroidlib.client.entity.UrlEncodedFormEntity;
import ch.boye.httpclientandroidlib.client.methods.HttpGet;
import ch.boye.httpclientandroidlib.client.methods.HttpPost;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy;
import ch.boye.httpclientandroidlib.cookie.Cookie;
import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient;
import ch.boye.httpclientandroidlib.impl.conn.tsccm.ThreadSafeClientConnManager;
import ch.boye.httpclientandroidlib.message.BasicHeaderElementIterator;
import ch.boye.httpclientandroidlib.params.BasicHttpParams;
import ch.boye.httpclientandroidlib.params.HttpConnectionParams;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.params.HttpProtocolParams;
import ch.boye.httpclientandroidlib.protocol.ExecutionContext;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.EntityUtils;

import com.gzit.utils.AndroidUtils;
import com.gzit.utils.StringUtils;

/**
 * 接管HTTP远程调用的过程。
 * 
 * @author teddy
 * 
 */
public abstract class HttpRemoter {

	public static final String TAG = "HttpRemoter";

	public static final int CONNECTION_TIMEOUT = 30 * 1000;
	public static final int SOCKET_TIMEOUT = 30 * 1000;
	public static final int MAX_CONNS_PER_ROUTE = 20;

	private static String youfangURL = "http://api.youfangonline.com";

	// http://youfang.api.sixthmedia.net/soufang/public/index.php?controller=house&action=gethouses&cityid=35

	private static HttpRemoter youfangRemoter;

	public static String mycookie = "";

	public static void syncCookies(Activity ctx) {
		List<Cookie> cookies = youfangRemoter.getCookie();

		if (!cookies.isEmpty()) {

			CookieSyncManager.createInstance(ctx);
			CookieManager cookieManager = CookieManager.getInstance();

			// sync all the cookies in the httpclient with the webview by
			// generating cookie string
			for (Cookie cookie : cookies) {
				String cookieString = cookie.getName() + "="
						+ cookie.getValue() + "; domain=" + cookie.getDomain();
				cookieManager.setCookie(cookie.getDomain(), cookieString);
				CookieSyncManager.getInstance().sync();
			}
		}

	}

	// 在系统初始，application创建的时候，初始化onlineURL
	public static void setupCloudServerEndPoint(String onlineURL) {
		HttpRemoter.youfangURL = onlineURL;

	}

	public static void clearCookie() {
		((DefaultHttpClient) getYoufangRemoter().getHttpClient())
				.getCookieStore().clear();
	}

	// ////////////////////外界通过该方法实现调用//////////////////////////
	public static void doRemote(Request req, Response resp) {
		switch (req.getType()) {
		case Request.TYPE_YOUFANG:
		default:
			getYoufangRemoter().doRequest(req, resp);
			return;

		}

		// throw new RuntimeException("unknown message type");

	}

	// ///////////////////////////////////////////////////////////
	// 在出现某些异常时的重试机制/////////////
	static Map<Request, Integer> resumeMapper = new HashMap<Request, Integer>();
	private static final int MAX_RESUME_TRY_COUNT = 2;

	private static boolean shouldTryToResumeFromException(Exception e,
			final Request req, final Response resp, final HttpRemoter remoter) {

		if (e != null && e.getMessage() != null
				&& e.getMessage().contains("Connection reset by peer")) {
			synchronized (resumeMapper) {
				Integer resumeCount = resumeMapper.get(req);
				if (resumeCount == null) {
					resumeMapper.put(req, 1);
				}
				resumeCount = resumeMapper.get(req);
				if (resumeCount > MAX_RESUME_TRY_COUNT) {
					resumeMapper.remove(req);
					return false;
				}
				resumeMapper.put(req, resumeCount + 1);
			}

			Log.i(TAG, "请求消息(" + req.getClass().getSimpleName()
					+ ")：失败，符合重试条件,正在重试...");
			doRequest(req, resp, remoter);
			return true;

		}
		synchronized (resumeMapper) {
			resumeMapper.remove(req);
		}

		return false;

	}

	// //////////////////////////////////////////////

	private static void doRequest(Request req, Response resp,
			HttpRemoter remoter) {

		// 路径合成
		String fullPath = remoter.getURL() + req.getActionPath();

		Log.i(TAG, "请求消息(" + req.descName() + ")：" + req.toString()
				+ " 请求完整路径：" + fullPath);

		byte[] resultBytes = null;

		// 发起HTTP请求
		try {

			// 构建消息
			HttpUriRequest httpRequest = null;
			switch (req.getMethod()) {
			case Message.METHOD_GET:
				StringBuffer sb = new StringBuffer(fullPath + "?");
				for (Pair pair : req.getParams()) {
					sb.append(pair.getName()
							+ "="
							+ (StringUtils.isBlank(pair.getValue()) ? ""
									: URLEncoder.encode(pair.getValue(),
											"UTF-8")));
					sb.append("&");
				}

				HttpGet get = new HttpGet(new URI(sb.toString()));
				httpRequest = get;
				break;
			case Message.METHOD_POST:
				HttpPost post = new HttpPost(new URI(fullPath));
				post.setEntity(new UrlEncodedFormEntity(req.getParams(),
						HTTP.UTF_8));
				httpRequest = post;

				break;

			}

			// 发起请求
			HttpResponse response = remoter.getHttpClient()
					.execute(httpRequest);

			// 处理http消息头
			resp.processHeader(response.getAllHeaders());

			// 获得返回并解析
			int statusCode = response.getStatusLine().getStatusCode();

			if (statusCode != 200) {
				resp.raiseException("HTTP状态码异常,状态码：" + statusCode);
				Log.i(TAG, "服务端正常返回，但是HTTP状态码异常：" + statusCode);
				resp.setExceptionStatusCode(statusCode);
				return;
			}

			resultBytes = EntityUtils.toByteArray(response.getEntity());

		} catch (Exception e) {

			if (!shouldTryToResumeFromException(e, req, resp, remoter)) {
				resp.raiseException("HTTP请求异常：" + e.getMessage());
				Log.i(TAG, "HTTP请求异常：" + e.getMessage());
			}

			return;
		}

		// 消息解码
		String respName = resp.descName();
		try {

			if (resultBytes != null) {
				resp.decode(resultBytes);
			}
		} catch (Exception e) {
			resp.raiseException(respName + "消息解析异常：" + e.getMessage());
			Log.i(TAG, respName + "消息解析异常：" + e.getMessage());
			return;

		}

		Log.i(TAG, respName + "消息已解析成功！");

	}

	static class CloudHttpRemoter extends HttpRemoter {

		private HttpClient httpClient;

		@Override
		public String getURL() {
			return youfangURL;
		}

		@Override
		public void doRequest(Request req, Response resp) {
			doRequest(req, resp, this);
		}

		@Override
		public HttpClient getHttpClient() {

			if (httpClient == null) {
				httpClient = createHttpClient();
			}

			return httpClient;
		}

		@Override
		public List<Cookie> getCookie() {
			return ((DefaultHttpClient) httpClient).getCookieStore()
					.getCookies();
		}

		@Override
		public void shutdown() {

			this.httpClient.getConnectionManager().shutdown();

		}

	}

	public static HttpRemoter getYoufangRemoter() {
		if (HttpRemoter.youfangRemoter == null) {
			HttpRemoter.youfangRemoter = new CloudHttpRemoter();
		}
		return HttpRemoter.youfangRemoter;

	}

	public abstract String getURL();

	public abstract HttpClient getHttpClient();

	public abstract List<Cookie> getCookie();

	public abstract void doRequest(Request req, Response resp);

	public abstract void shutdown();

	private HttpRemoter() {

	}

	private static DefaultHttpClient createHttpClient() {

		HttpParams httpParams = new BasicHttpParams();

		// http版本设置
		HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
		HttpProtocolParams.setContentCharset(httpParams, HTTP.UTF_8);

		// 自定义User-Agent消息头
		HttpProtocolParams.setUserAgent(httpParams, genUserAgentHeader());

		// 关闭HTTP client发的continue100数据指令，避免服务器不支持导致417异常
		HttpProtocolParams.setUseExpectContinue(httpParams, false);

		// 连接超时设置
		HttpConnectionParams.setConnectionTimeout(httpParams,
				CONNECTION_TIMEOUT);

		// 读取数据超时设置
		HttpConnectionParams.setSoTimeout(httpParams, SOCKET_TIMEOUT);

		ThreadSafeClientConnManager conMgr = new ThreadSafeClientConnManager();
		conMgr.setDefaultMaxPerRoute(MAX_CONNS_PER_ROUTE);
//		conMgr.log.enableDebug(true);
		DefaultHttpClient httpclient = new DefaultHttpClient(conMgr, httpParams);
//		httpclient.log.enableDebug(true);
		
		httpclient.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {

		    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
		        // Honor 'keep-alive' header
		        HeaderElementIterator it = new BasicHeaderElementIterator(
		                response.headerIterator(HTTP.CONN_KEEP_ALIVE));
		        while (it.hasNext()) {
		            HeaderElement he = it.nextElement();
		            String param = he.getName(); 
		            String value = he.getValue();
		            if (value != null && param.equalsIgnoreCase("timeout")) {
		                try {
		                    return Long.parseLong(value) * 1000;
		                } catch(NumberFormatException ignore) {
		                }
		            }
		        }
				// HttpHost target = (HttpHost) context.getAttribute(
				// ExecutionContext.HTTP_TARGET_HOST);
		      
		        
		        return 10*1000;
		    }
		    
		});

		
		return httpclient; 

	}

	// shutdown remoters when application is going to terminate
	public static void shutdownRemoters() {

		if (youfangRemoter != null) {
			youfangRemoter.shutdown();
		}

	}

	// appclientid/version;os;brand;model
	public static String genUserAgentHeader() {
		return AndroidUtils.System.getVersionName() + ";" + "Android"
				+ Build.VERSION.RELEASE + ";" + Build.BRAND + ";" + Build.MODEL;

	}

}
